#!/usr/bin/env python

import os

def error_and_exit(*msg):
    from sys import exit
    import logging
    logging.getLogger('core').critical('\n'.join(msg))
    exit(1)


def load_config(config_file=None):
    from os import path
    from fgs.proxy.config import FgsConfigParser as ConfigParser

    if config_file == None:
        home = os.path.expanduser(u'~')
        if os.name == 'nt':
            data_dir = '_fgs'
        else:
            data_dir = '.fgs'
        config_file = os.path.join(home, data_dir, 'proxy.conf')

    file_path = path.abspath(config_file)
    if not path.exists(file_path):
        from fgs.proxy.config import config_data, motd_data
        from sys import exit

        print "Configuration file %s not found." % config_file
        print "If you like, I can write a default configuration for you to look at."
        print ("This will write the config file at the location specified, "+
               "and also create a proxy.motd file in the same directory.")
        print
        reply = raw_input("Do you want me to write a "+
                          "default configuration for you? ")
        if reply in ('yes', 'y'):
            file_dir = os.path.dirname(file_path)
            if not path.exists(file_dir):
                os.makedirs(file_dir)

            f = file(file_path, 'w')
            f.write(config_data)
            f.close()

            if path.exists(path.join(path.dirname(file_path), 'proxy.motd')):
                print "Not creating proxy.motd: file exists."
            else:
                f = file(path.join(path.dirname(file_path), 'proxy.motd'), 'w')
                f.write(motd_data)
                f.close()

            print "Default configuration created."
            print ""
            print "EDIT <", file_path, "> BEFORE START PROXY !"
            print ""
            exit(0)
        else:
            print "Not creating default configuration."
            exit(1)

    try:
        conf = ConfigParser()
        conf.readfp(open(file_path))
    except Exception, e:
        error_and_exit("Failed to read %s: %s" % (config_file, e))

    return conf


def setup_logging(config):
    from twisted.python import log as tw_log
    import logging
    import sys

    TWISTED_DEBUG_LOGLEVEL = 5

    #
    # Check the configuration for validity
    #
    config.check_fields(error_and_exit,
                        ('proxy', 'daemonize'),
                        ('logging', 'logfile'),
                        ('logging', 'level'))

    daemonize = config.getboolean('proxy', 'daemonize')
    logfile = config.get('logging', 'logfile')
    loglevel = config.get('logging', 'level')

    if daemonize and logfile == "@console":
        error_and_exit("Cannot log to console in daemon mode.")

    valid_loglevels = ["critical", "error", "warning", "info",
                       "debug", "fulldebug"]
    levels = dict(zip(valid_loglevels,
                      (logging.CRITICAL, logging.ERROR,
                       logging.WARNING, logging.INFO,
                       logging.DEBUG, TWISTED_DEBUG_LOGLEVEL)))
    if loglevel not in valid_loglevels:
        error_and_exit("Invalid loglevel %s" % loglevel,
                       "loglevel must be one of:",
                       "  %s" % ', '.join(valid_loglevels))

    loglevel = levels[loglevel]

    #
    # Setup Python logging
    #
    log_fmt = "%(asctime)s %(levelname)s (%(name)s) %(message)s"
    date_fmt = "%Y%m%d %H:%M:%S"

    logging.addLevelName(TWISTED_DEBUG_LOGLEVEL, "TWISTED")

    if logfile == "@console":
        output = logging.StreamHandler(sys.stderr)
    else:
        output = logging.FileHandler(logfile)

    output.setLevel(TWISTED_DEBUG_LOGLEVEL)
    formatter = logging.Formatter(log_fmt, date_fmt)
    output.setFormatter(formatter)
    logging.getLogger('').addHandler(output)

    logging.getLogger('').setLevel(loglevel)
    logging.getLogger('core').info("Logger initialized")

    #
    # Set up twisted logging to redirect through python logging
    #
    def log_twisted_to_python(event):
        if event['isError']:
            level = logging.ERROR
        else:
            level = TWISTED_DEBUG_LOGLEVEL

        if event['system'] == "-":
            system = "twisted"
        else:
            system = "twisted.%s" % event['system']

        if event.has_key('message'):
            for l in event['message']:
                logging.getLogger(system).log(level, l)
        if event.has_key('failure'):
            logging.getLogger(system).log(level, str(event['failure']))

    tw_log.startLoggingWithObserver(log_twisted_to_python,
                                    setStdout=0)


def make_application(config):
    from twisted.application.service import Application, IServiceCollection
    from twisted.application.internet import TCPServer
    from fgs.proxy.error import FgsProxyError
    from fgs.proxy.gameproxy import GameProxy
    from fgs.proxy.interface.game import FgsLibFactory

    #
    # Check the configuration for validity
    #
    config.check_fields(error_and_exit,
                        ('proxy', 'backend'),
                        ('ssh', 'enable'))

    backend_name = config.get('proxy', 'backend')
    ssh_enable = config.getboolean('ssh', 'enable')

    #
    # Import the correct backend
    #
    def import_backend(name):
        name = "fgs.proxy.backend." + name
        mod = __import__(name)
        components = name.split('.')
        for comp in components[1:]:
            mod = getattr(mod, comp)
        return getattr(mod, 'backend_class')

    backend_class = import_backend(backend_name)

    #
    # Create the fgs proxy services
    #
    try:
        backend = backend_class(config)
        proxy = GameProxy(config, backend)
        factories = []
        factories.append((6567, FgsLibFactory(proxy)))
        if ssh_enable:
            from fgs.proxy.interface.ssh import FgsSSHFactory
            factories.append((6568, FgsSSHFactory(proxy)))
    except FgsProxyError, e:
        error_and_exit(str(e))

    #
    # Create the application instance and attach the services
    #
    application = Application("proxy")
    proxy.setServiceParent(application)
    for factory in factories:
        TCPServer(*factory).setServiceParent(proxy)

    return application


def start_proxy():
    from twisted.internet import reactor
    from twisted.application import app, service
    from twisted.scripts.twistd import daemonize
    from sys import argv
    import logging

    if len(argv) == 2:
        conf = load_config(argv[1])
    else:
        conf = load_config()

    setup_logging(conf)

    log = logging.getLogger('core')

    application = make_application(conf)

    if conf.getboolean('proxy', 'daemonize'):
        log.info("Forking into background")
        daemonize()

    # Boot the application
    app_service = service.IService(application)
    app_service.privilegedStartService()
    app_service.startService()
    reactor.addSystemEventTrigger('before', 'shutdown',
                                  app_service.stopService)

    try:
        reactor.run()
    except:
        import traceback
        traceback.print_exc()
    log.info("Proxy shutting down.")

def run():
    start_proxy()
